using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;

namespace TAAlert.BackTest
{
    class BackTestDataTableBuilder
    {
        private static readonly string N_COL = "n";
        private static readonly string N_SHORT_COL = "nShort";
        private static readonly string N_LONG_COL = "nLong";
        private static readonly string N_FAST_COL = "nFast";
        private static readonly string VOL_TREASHOLD_COL = "volT";
        private static readonly string TOT_PL_COL = "P/L Tot";
        private static readonly string TOT_MAXDD_COL = "MaxDD Tot";
        private static readonly string TOT_CALMAR_COL = "Calmar Tot";
        
        public static readonly string FORMAT_EXT_PROPERTY = "Format";

        private static readonly TraceSwitch traceM = new TraceSwitch("BackTestDataTableBuilder", "Debug messages in BackTestDataTableBuilder");
        private DateTime theFirstDateM; // Used first date, not necessarily startDate 
        private DateTime theLastDateM;  // Used last date not necessarily endDate    

        public BackTestDataTableBuilder()
        {
        }

        public DateTime FirstDate
        {
            get { return this.theFirstDateM; }
        }

        public DateTime LastDate
        {
            get { return this.theLastDateM; }
        }

        /// <summary>Create DataTable with (single) EMA back-test</summary>
        /// <param name="emaPerfList">List of parameters and corresponding performance summaries</param>
        /// <param name="showYearlyPerf">Is yearly performance to be shown?</param>
        /// <param name="showMonthlyPerf">Is monthly performance to be shown?</param>
        /// <returns>Data table with n-period in the first column, P/L, MaxDD and Calmar in next columns and Yearly and MOnthly Calmars in remaining columns</returns>
        public DataTable createEMABackTest(SortedList<int, BackTestManager.PerfSummary> perf, bool showYearlyPerf, bool showMonthlyPerf)
        {
            // Create table
            DataTable dTable = new DataTable();

            // Create columns
            dTable.Columns.Add(N_COL, typeof(int)).ExtendedProperties.Add(FORMAT_EXT_PROPERTY, "N0");
            dTable.Columns.Add(TOT_PL_COL, typeof(double)).ExtendedProperties.Add(FORMAT_EXT_PROPERTY, "N2");
            dTable.Columns.Add(TOT_MAXDD_COL, typeof(double)).ExtendedProperties.Add(FORMAT_EXT_PROPERTY, "N2");
            dTable.Columns.Add(TOT_CALMAR_COL, typeof(double)).ExtendedProperties.Add(FORMAT_EXT_PROPERTY, "N2");

            // Create rows
            bool createValueColumns = true;
            foreach (KeyValuePair<int, BackTestManager.PerfSummary> nSumPair in perf)
            {
                DataRow row = dTable.NewRow();
                row[N_COL] = nSumPair.Key;

                BackTestManager.PerfSummary summary = nSumPair.Value;
                this.addValueColumns(row, dTable, summary, showYearlyPerf, showMonthlyPerf, createValueColumns);
                createValueColumns = false;
            }

            return dTable;
        }

        /// <summary>Create DataTable with VEMA back-test results</summary>
        /// <param name="vemaPerf"></param>
        /// <param name="showYearlyPerf"></param>
        /// <param name="showMonthlyPerf"></param>
        /// <returns></returns>
        public DataTable createVEMATable(List<BackTestManager.VEMAPerfSummary> vemaPerf, bool showYearlyPerf, bool showMonthlyPerf)
        {
            // Create table
            DataTable dTable = new DataTable();
            // Create columns
            dTable.Columns.Add(N_SHORT_COL, typeof(int)).ExtendedProperties.Add(FORMAT_EXT_PROPERTY, "N0");
            dTable.Columns.Add(N_LONG_COL, typeof(int)).ExtendedProperties.Add(FORMAT_EXT_PROPERTY, "N0");
            dTable.Columns.Add(VOL_TREASHOLD_COL, typeof(double)).ExtendedProperties.Add(FORMAT_EXT_PROPERTY, "P1");
            dTable.Columns.Add(TOT_PL_COL, typeof(double)).ExtendedProperties.Add(FORMAT_EXT_PROPERTY, "N2");
            dTable.Columns.Add(TOT_MAXDD_COL, typeof(double)).ExtendedProperties.Add(FORMAT_EXT_PROPERTY, "N2");
            dTable.Columns.Add(TOT_CALMAR_COL, typeof(double)).ExtendedProperties.Add(FORMAT_EXT_PROPERTY, "N2");

            // Create rows
            bool createValueColumns = true;   // are all monthly/yearly columns already created?
            foreach (BackTestManager.VEMAPerfSummary vemaSummary in vemaPerf)
            {
                DataRow row = dTable.NewRow();
                row[N_SHORT_COL] = vemaSummary.NEma1;
                row[N_LONG_COL] = vemaSummary.NEma2;
                row[VOL_TREASHOLD_COL] = vemaSummary.VolTreashold;

                this.addValueColumns(row, dTable, vemaSummary.Summary, showYearlyPerf, showMonthlyPerf, createValueColumns);

                createValueColumns = false; // columns are already created by now
            }
            return dTable;
        }

        /// <summary>Create DataTable with TwoEMA back-test results</summary>
        public DataTable createMACDDataTable(List<BackTestManager.MACDPerfSummary> macdPerf, bool showYearlyPerf, bool showMonthlyPerf)
        {
            // Create table
            DataTable dTable = new DataTable();
            // Create columns
            dTable.Columns.Add(N_SHORT_COL, typeof(int)).ExtendedProperties.Add(FORMAT_EXT_PROPERTY, "N0");
            dTable.Columns.Add(N_LONG_COL, typeof(int)).ExtendedProperties.Add(FORMAT_EXT_PROPERTY, "N0");
            dTable.Columns.Add(N_FAST_COL, typeof(int)).ExtendedProperties.Add(FORMAT_EXT_PROPERTY, "N0");
            dTable.Columns.Add(TOT_PL_COL, typeof(double)).ExtendedProperties.Add(FORMAT_EXT_PROPERTY, "N2");
            dTable.Columns.Add(TOT_MAXDD_COL, typeof(double)).ExtendedProperties.Add(FORMAT_EXT_PROPERTY, "N2");
            dTable.Columns.Add(TOT_CALMAR_COL, typeof(double)).ExtendedProperties.Add(FORMAT_EXT_PROPERTY, "N2");

            // Create rows
            bool createValueColumns = true;   // are all monthly/yearly columns already created?
            foreach (BackTestManager.MACDPerfSummary macdSummary in macdPerf)
            {
                DataRow row = dTable.NewRow();
                row[N_SHORT_COL] = macdSummary.NEmaShort;
                row[N_LONG_COL] = macdSummary.NEmaLong;
                row[N_FAST_COL] = macdSummary.NFast;

                this.addValueColumns(row, dTable, macdSummary.Summary, showYearlyPerf, showMonthlyPerf, createValueColumns);

                createValueColumns = false; // columns are already created by now
            }
            return dTable;
        }

        /// <summary>Create DataTable with TwoEMA back-test results</summary>
        /// <param name="twoEmaPerf">List of PerfSummaries for all combinations of nShort, nLong</param>
        /// <param name="showYearlyPerf">Is yearly performance to be shown?</param>
        /// <param name="showMonthlyPerf">Is monthly performance to be shown?</param>
        /// <returns>Data-table with nShort and nLong EMA periods inthe first 2 columns and P/L, MaxDD and Calmar in columns 3,4,5 and Yearly and monthly Calmar ratios in remaining columns</returns>
        public DataTable createTwoEMADataTable(List<BackTestManager.TwoEmaPerfSummary> twoEmaPerf, bool showYearlyPerf, bool showMonthlyPerf)
        {
            // Create table
            DataTable dTable = new DataTable();
            // Create columns
            dTable.Columns.Add(N_SHORT_COL, typeof(int)).ExtendedProperties.Add(FORMAT_EXT_PROPERTY, "N0");
            dTable.Columns.Add(N_LONG_COL, typeof(int)).ExtendedProperties.Add(FORMAT_EXT_PROPERTY, "N0");
            dTable.Columns.Add(VOL_TREASHOLD_COL, typeof(double)).ExtendedProperties.Add(FORMAT_EXT_PROPERTY, "P1");
            dTable.Columns.Add(TOT_PL_COL, typeof(double)).ExtendedProperties.Add(FORMAT_EXT_PROPERTY, "N2");
            dTable.Columns.Add(TOT_MAXDD_COL, typeof(double)).ExtendedProperties.Add(FORMAT_EXT_PROPERTY, "N2");
            dTable.Columns.Add(TOT_CALMAR_COL, typeof(double)).ExtendedProperties.Add(FORMAT_EXT_PROPERTY, "N2");

            // Create rows
            bool createValueColumns = true;   // are all monthly/yearly columns already created?
            foreach (BackTestManager.TwoEmaPerfSummary twoEmaSummary in twoEmaPerf)
            {
                DataRow row = dTable.NewRow();
                row[N_SHORT_COL] = twoEmaSummary.NEmaShort;
                row[N_LONG_COL] = twoEmaSummary.NEmaLong;
                row[VOL_TREASHOLD_COL] = twoEmaSummary.VolatilityTreashold;

                this.addValueColumns(row, dTable, twoEmaSummary.Summary, showYearlyPerf, showMonthlyPerf, createValueColumns);

                createValueColumns = false; // columns are already created by now
            }
            return dTable;
        }

        /// <summary>Add total/yearly/monthly performance value columns to the row. If requested create the new columns</summary>
        /// <param name="row">DataRow to add columns into</param>
        /// <param name="dTable">DataTable to add Columns and the ROW</param>
        /// <param name="summary">PerfSummary with Total, Yearly and MOnthly data</param>
        /// <param name="showYearlyPerf">Is yearly performance to be shown?</param>
        /// <param name="showMonthlyPerf">Is monthly performance to be shown?</param>
        /// <param name="createValueColumns">If TRUE then create new calue columns (only for the first row)</param>
        private void addValueColumns(DataRow row, DataTable dTable, BackTestManager.PerfSummary summary, bool showYearlyPerf, bool showMonthlyPerf, bool createValueColumns)
        {
            // Total Columns
            PerfStatistics totStat = summary.Total;
            row[TOT_PL_COL] = totStat.ProfitLoss;
            row[TOT_MAXDD_COL] = totStat.MaxDrawDown;
            row[TOT_CALMAR_COL] = totStat.Calmar;

            // Update the first and the last dates
            if (createValueColumns)
            {
                this.theFirstDateM = totStat.StartDate;
                this.theLastDateM = totStat.EndDate;
            }

            // Yearly Columns
            if (showYearlyPerf)
            {
                List<PerfStatistics> perfYearly = summary.Yearly;
                List<double> calmY = new List<double>(perfYearly.Count);

                foreach (PerfStatistics stat in perfYearly)
                {
                    Debug.WriteLineIf(traceM.TraceVerbose, "yearly: date=" + stat.EndDate.ToShortDateString());
                    string colName = "Calmar " + stat.EndDate.Year.ToString();
                    if (createValueColumns)
                    {
                        Debug.WriteLineIf(traceM.TraceInfo, "Adding new YEARLY column: " + colName);
                        dTable.Columns.Add(colName, typeof(double)).ExtendedProperties.Add(FORMAT_EXT_PROPERTY, "N2");
                    }
                    row[colName] = stat.Calmar;
                    calmY.Add(stat.Calmar);
                }
                createScoreColumn(dTable, row, calmY, "YScore", createValueColumns);
            }

            // Monthly Columns
            if (showMonthlyPerf)
            {
                List<PerfStatistics> perfMonthly = summary.Monthly;
                List<double> calmM = new List<double>(perfMonthly.Count);
                foreach (PerfStatistics stat in perfMonthly)
                {
                    Debug.WriteLineIf(traceM.TraceVerbose, "monthly: date=" + stat.EndDate.ToShortDateString());
                    string colName = "Calmar " + stat.EndDate.ToString("MMMM yy");
                    if (createValueColumns)
                    {
                        Debug.WriteLineIf(traceM.TraceInfo, "Adding new MONTHLY column " + colName);
                        dTable.Columns.Add(colName, typeof(double)).ExtendedProperties.Add(FORMAT_EXT_PROPERTY, "N2");
                    }
                    row[colName] = stat.Calmar;
                    calmM.Add(stat.Calmar);
                }
                createScoreColumn(dTable, row, calmM, "MScore", createValueColumns);
            }

            dTable.Rows.Add(row);
        }

        /// <summary>Create new score column based on weighted average of Calmar rations</summary>
        private void createScoreColumn(DataTable dTable, DataRow row, List<double> calmars, string colName, bool createValueColumns)
        {
            double score = calculateScore(calmars);
            if (createValueColumns)
            {
                Debug.WriteLineIf(traceM.TraceInfo, "Adding new score column: " + colName);
                dTable.Columns.Add(colName, typeof(double)).ExtendedProperties.Add(FORMAT_EXT_PROPERTY, "N2");
            }
            row[colName] = score;
        }

        /// <summary>Calculate weighted average of numbers. Linear model: The last element is n-times as important as the first</summary>
        /// <param name="x">List of doubles to be weighted averaged</param>
        /// <returns>Score based on linear weighted average</returns>
        private double calculateScore(List<double> x)
        {
            int n = x.Count;
            double score = 0;
            for (int i = 1; i <= n; ++i)
                score += 2.0 * i / (n * (n + 1)) * x[i - 1];
            return score;
        }

    }
}
